Skip to content

Conversation

@bogner
Copy link
Contributor

@bogner bogner commented Dec 23, 2025

Raw (as in ByteAddress) buffer accesses in DXIL must specify ElementIndex as undef, and Structured buffer accesses must specify a value. Ensure that we do this correctly in DXILResourceAccess, and enforce that the operations are valid in DXILOpLowering.

Fixes #173316

Raw (as in ByteAddress) buffer accesses in DXIL must specify
ElementIndex as undef, and Structured buffer accesses must specify a
value. Ensure that we do this correctly in DXILResourceAccess, and
enforce that the operations are valid in DXILOpLowering.

Fixes llvm#173316
@llvmbot
Copy link
Member

llvmbot commented Dec 23, 2025

@llvm/pr-subscribers-backend-directx

Author: Justin Bogner (bogner)

Changes

Raw (as in ByteAddress) buffer accesses in DXIL must specify ElementIndex as undef, and Structured buffer accesses must specify a value. Ensure that we do this correctly in DXILResourceAccess, and enforce that the operations are valid in DXILOpLowering.

Fixes #173316


Patch is 23.26 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/173320.diff

10 Files Affected:

  • (modified) llvm/docs/DirectX/DXILResources.rst (+8-3)
  • (modified) llvm/lib/Target/DirectX/DXILOpLowering.cpp (+27)
  • (modified) llvm/lib/Target/DirectX/DXILResourceAccess.cpp (+37-10)
  • (modified) llvm/test/CodeGen/DirectX/BufferLoad-sm61.ll (+2-2)
  • (modified) llvm/test/CodeGen/DirectX/BufferStore-sm61.ll (+4-4)
  • (added) llvm/test/CodeGen/DirectX/RawBuffer-errors.ll (+78)
  • (modified) llvm/test/CodeGen/DirectX/RawBufferLoad.ll (+6-6)
  • (modified) llvm/test/CodeGen/DirectX/RawBufferStore.ll (+6-6)
  • (modified) llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll (+3-3)
  • (modified) llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll (+3-3)
diff --git a/llvm/docs/DirectX/DXILResources.rst b/llvm/docs/DirectX/DXILResources.rst
index db7d4a4342eb7..fb8d6fa580c67 100644
--- a/llvm/docs/DirectX/DXILResources.rst
+++ b/llvm/docs/DirectX/DXILResources.rst
@@ -402,6 +402,11 @@ which matches DXIL. Unlike in the `RawBufferLoad`_ operation, we do not need
 arguments for the mask/type size and alignment, since we can calculate these
 from the return type of the load during lowering.
 
+Note that RawBuffer loads represent either "structured" accesses, as in HLSL's
+StructuredBuffer<T>, or a "raw" access, as in HLSL's "ByteAddressBuffer". The
+`%offset` parameter is only used for structured accesses, and *must* be
+`poison` for raw accesses.
+
 .. _RawBufferLoad: https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#rawbufferload
 
 .. list-table:: ``@llvm.dx.resource.load.rawbuffer``
@@ -442,7 +447,7 @@ Examples:
        @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(
            target("dx.RawBuffer", i8, 0, 0, 0) %buffer,
            i32 %byte_offset,
-           i32 0)
+           i32 poison)
 
    ; float4
    %ret = call {<4 x float>, i1}
@@ -454,7 +459,7 @@ Examples:
        @llvm.dx.resource.load.rawbuffer.v4f32.tdx.RawBuffer_i8_0_0_0t(
            target("dx.RawBuffer", i8, 0, 0, 0) %buffer,
            i32 %byte_offset,
-           i32 0)
+           i32 poison)
 
    ; struct S0 { float4 f; int4 i; };
    %ret = call {<4 x float>, i1}
@@ -488,7 +493,7 @@ Examples:
        @llvm.dx.resource.load.rawbuffer.v4i64.tdx.RawBuffer_i8_0_0t(
            target("dx.RawBuffer", i8, 0, 0, 0) %buffer,
            i32 %byte_offset,
-           i32 0)
+           i32 poison)
 
 Stores
 ------
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index e46a393e50906..d1cc2db59190f 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -272,6 +272,21 @@ class OpLowerer {
     return false;
   }
 
+  Error validateRawBufferElementIndex(Value *Resource, Value *ElementIndex) {
+    bool IsStruct = cast<RawBufferExtType>(Resource->getType())->isStructured();
+    bool IsPoison = isa<PoisonValue>(ElementIndex);
+
+    if (IsStruct && IsPoison)
+      return make_error<StringError>(
+          "Element index of structured buffer may not be poison",
+          inconvertibleErrorCode());
+    else if (!IsStruct && !IsPoison)
+      return make_error<StringError>(
+          "Element index of raw buffer must be poison",
+          inconvertibleErrorCode());
+    return Error::success();
+  }
+
   [[nodiscard]] bool lowerToCreateHandle(Function &F) {
     IRBuilder<> &IRB = OpBuilder.getIRB();
     Type *Int8Ty = IRB.getInt8Ty();
@@ -560,6 +575,11 @@ class OpLowerer {
       Value *Align =
           ConstantInt::get(Int32Ty, DL.getPrefTypeAlign(ScalarTy).value());
 
+      if (Error E = validateRawBufferElementIndex(CI->getOperand(0), Index1))
+        return E;
+      if (isa<PoisonValue>(Index1))
+        Index1 = UndefValue::get(Index1->getType());
+
       Expected<CallInst *> OpCall =
           MMDI.DXILVersion >= VersionTuple(1, 2)
               ? OpBuilder.tryCreateOp(OpCode::RawBufferLoad,
@@ -671,6 +691,13 @@ class OpLowerer {
       Value *Index0 = CI->getArgOperand(1);
       Value *Index1 = IsRaw ? CI->getArgOperand(2) : UndefValue::get(Int32Ty);
 
+      if (IsRaw) {
+        if (Error E = validateRawBufferElementIndex(CI->getOperand(0), Index1))
+          return E;
+        if (isa<PoisonValue>(Index1))
+          Index1 = UndefValue::get(Index1->getType());
+      }
+
       Value *Data = CI->getArgOperand(IsRaw ? 3 : 2);
       Type *DataTy = Data->getType();
       Type *ScalarTy = DataTy->getScalarType();
diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 37a7c0f572c69..28d1ffb7ca6f0 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -120,19 +120,33 @@ static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI,
   SI->replaceAllUsesWith(Inst);
 }
 
-static void createRawStore(IntrinsicInst *II, StoreInst *SI) {
+static void createRawStore(IntrinsicInst *II, StoreInst *SI,
+                           dxil::ResourceTypeInfo &RTI) {
   const DataLayout &DL = SI->getDataLayout();
   IRBuilder<> Builder(SI);
 
   Value *V = SI->getValueOperand();
+  assert(!V->getType()->isAggregateType() &&
+         "Resource store should be scalar or vector type");
+
+  Value *Index = II->getOperand(1);
   // The offset for the rawbuffer load and store ops is always in bytes.
   uint64_t AccessSize = 1;
   Value *Offset =
       traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
-  // TODO: break up larger types
-  auto *Inst = Builder.CreateIntrinsic(
-      Builder.getVoidTy(), Intrinsic::dx_resource_store_rawbuffer,
-      {II->getOperand(0), II->getOperand(1), Offset, V});
+
+  // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
+  // entirely into the index.
+  if (!RTI.isStruct()) {
+    auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
+    if (!ConstantOffset || !ConstantOffset->isZero())
+      Index = Builder.CreateAdd(Index, Offset);
+    Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
+  }
+
+  auto *Inst = Builder.CreateIntrinsic(Builder.getVoidTy(),
+                                       Intrinsic::dx_resource_store_rawbuffer,
+                                       {II->getOperand(0), Index, Offset, V});
   SI->replaceAllUsesWith(Inst);
 }
 
@@ -143,7 +157,7 @@ static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI,
     return createTypedBufferStore(II, SI, RTI);
   case dxil::ResourceKind::RawBuffer:
   case dxil::ResourceKind::StructuredBuffer:
-    return createRawStore(II, SI);
+    return createRawStore(II, SI, RTI);
   case dxil::ResourceKind::Texture1D:
   case dxil::ResourceKind::Texture2D:
   case dxil::ResourceKind::Texture2DMS:
@@ -198,20 +212,33 @@ static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI,
   LI->replaceAllUsesWith(V);
 }
 
-static void createRawLoad(IntrinsicInst *II, LoadInst *LI) {
+static void createRawLoad(IntrinsicInst *II, LoadInst *LI,
+                          dxil::ResourceTypeInfo &RTI) {
   const DataLayout &DL = LI->getDataLayout();
   IRBuilder<> Builder(LI);
 
-  // TODO: break up larger types
   Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty());
+  assert(!LI->getType()->isAggregateType() &&
+         "Resource load should be scalar or vector type");
 
+  Value *Index = II->getOperand(1);
   // The offset for the rawbuffer load and store ops is always in bytes.
   uint64_t AccessSize = 1;
   Value *Offset =
       traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
+
+  // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
+  // entirely into the index.
+  if (!RTI.isStruct()) {
+    auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
+    if (!ConstantOffset || !ConstantOffset->isZero())
+      Index = Builder.CreateAdd(Index, Offset);
+    Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
+  }
+
   Value *V =
       Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,
-                              {II->getOperand(0), II->getOperand(1), Offset});
+                              {II->getOperand(0), Index, Offset});
   V = Builder.CreateExtractValue(V, {0});
 
   LI->replaceAllUsesWith(V);
@@ -367,7 +394,7 @@ static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI,
     return createTypedBufferLoad(II, LI, RTI);
   case dxil::ResourceKind::RawBuffer:
   case dxil::ResourceKind::StructuredBuffer:
-    return createRawLoad(II, LI);
+    return createRawLoad(II, LI, RTI);
   case dxil::ResourceKind::CBuffer:
     return createCBufferLoad(II, LI, RTI);
   case dxil::ResourceKind::Texture1D:
diff --git a/llvm/test/CodeGen/DirectX/BufferLoad-sm61.ll b/llvm/test/CodeGen/DirectX/BufferLoad-sm61.ll
index b433bcee9029c..51c025262970c 100644
--- a/llvm/test/CodeGen/DirectX/BufferLoad-sm61.ll
+++ b/llvm/test/CodeGen/DirectX/BufferLoad-sm61.ll
@@ -25,12 +25,12 @@ define void @loadv4f32_byte(i32 %offset) {
       @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
           i32 0, i32 0, i32 1, i32 0, ptr null)
 
-  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle %{{.*}}, i32 %offset, i32 0)
+  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle %{{.*}}, i32 %offset, i32 undef)
   %load = call {<4 x float>, i1}
       @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(
           target("dx.RawBuffer", i8, 0, 0, 0) %buffer,
           i32 %offset,
-          i32 0)
+          i32 poison)
 
   ret void
 }
diff --git a/llvm/test/CodeGen/DirectX/BufferStore-sm61.ll b/llvm/test/CodeGen/DirectX/BufferStore-sm61.ll
index 188ac75c5d1ab..b945b6ca82513 100644
--- a/llvm/test/CodeGen/DirectX/BufferStore-sm61.ll
+++ b/llvm/test/CodeGen/DirectX/BufferStore-sm61.ll
@@ -23,10 +23,10 @@ define void @storef32_byte(i32 %offset, float %data) {
       @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
           i32 0, i32 0, i32 1, i32 0, ptr null)
 
-  ; CHECK: call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %{{.*}}, i32 %offset, i32 0, float %data, float undef, float undef, float undef, i8 1)
+  ; CHECK: call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %{{.*}}, i32 %offset, i32 undef, float %data, float undef, float undef, float undef, i8 1)
   call void @llvm.dx.resource.store.rawbuffer.f32(
       target("dx.RawBuffer", i8, 1, 0, 0) %buffer,
-      i32 %offset, i32 0, float %data)
+      i32 %offset, i32 poison, float %data)
 
   ret void
 }
@@ -59,10 +59,10 @@ define void @storev4f32_byte(i32 %offset, <4 x float> %data) {
   ; CHECK: [[DATA1:%.*]] = extractelement <4 x float> %data, i32 1
   ; CHECK: [[DATA2:%.*]] = extractelement <4 x float> %data, i32 2
   ; CHECK: [[DATA3:%.*]] = extractelement <4 x float> %data, i32 3
-  ; CHECK: call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %{{.*}}, i32 %offset, i32 0, float [[DATA0]], float [[DATA1]], float [[DATA2]], float [[DATA3]], i8 15)
+  ; CHECK: call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %{{.*}}, i32 %offset, i32 undef, float [[DATA0]], float [[DATA1]], float [[DATA2]], float [[DATA3]], i8 15)
   call void @llvm.dx.resource.store.rawbuffer.v4f32(
       target("dx.RawBuffer", i8, 1, 0, 0) %buffer,
-      i32 %offset, i32 0, <4 x float> %data)
+      i32 %offset, i32 poison, <4 x float> %data)
 
   ret void
 }
diff --git a/llvm/test/CodeGen/DirectX/RawBuffer-errors.ll b/llvm/test/CodeGen/DirectX/RawBuffer-errors.ll
new file mode 100644
index 0000000000000..b413cf1dfe4dc
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/RawBuffer-errors.ll
@@ -0,0 +1,78 @@
+; We use llc for this test so that we don't abort after the first error.
+; RUN: not llc %s -o /dev/null 2>&1 | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+declare void @f32_user(float)
+
+; CHECK: error:
+; CHECK-SAME: in function loadrawzero
+; CHECK-SAME: Element index of raw buffer must be poison
+define void @loadrawzero(i32 %offset) "hlsl.export" {
+  %buffer = call target("dx.RawBuffer", i8, 0, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  %load = call {float, i1}
+      @llvm.dx.resource.load.rawbuffer(
+          target("dx.RawBuffer", i8, 0, 0, 0) %buffer,
+          i32 %offset,
+          i32 0)
+  %data = extractvalue {float, i1} %load, 0
+
+  call void @f32_user(float %data)
+
+  ret void
+}
+
+; CHECK: error:
+; CHECK-SAME: in function loadstructundef
+; CHECK-SAME: Element index of structured buffer may not be poison
+define void @loadstructundef(i32 %index) "hlsl.export" {
+  %buffer = call target("dx.RawBuffer", float, 0, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  %load = call {float, i1}
+      @llvm.dx.resource.load.rawbuffer(
+          target("dx.RawBuffer", float, 0, 0, 0) %buffer,
+          i32 %index,
+          i32 poison)
+  %data = extractvalue {float, i1} %load, 0
+  call void @f32_user(float %data)
+
+  ret void
+}
+
+; CHECK: error:
+; CHECK-SAME: in function storerawzero
+; CHECK-SAME: Element index of raw buffer must be poison
+define void @storerawzero(i32 %offset, float %data) {
+  %buffer = call target("dx.RawBuffer", i8, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(
+          i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  call void @llvm.dx.resource.store.rawbuffer(
+      target("dx.RawBuffer", i8, 1, 0, 0) %buffer,
+      i32 %offset, i32 0, float %data)
+
+  ret void
+}
+
+; CHECK: error:
+; CHECK-SAME: in function storestructundef
+; CHECK-SAME: Element index of structured buffer may not be poison
+define void @storestructundef(i32 %index, float %data) {
+  %buffer = call target("dx.RawBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(
+          i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  call void @llvm.dx.resource.store.rawbuffer(
+      target("dx.RawBuffer", float, 1, 0, 0) %buffer,
+      i32 %index, i32 poison, float %data)
+
+  ret void
+}
+
+declare { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(target("dx.RawBuffer", i8, 0, 0, 0), i32, i32)
+declare { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_f32_0_0_0t(target("dx.RawBuffer", float, 0, 0, 0), i32, i32)
+declare void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i8_1_0_0t.f32(target("dx.RawBuffer", i8, 1, 0, 0), i32, i32, float)
+declare void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_f32_1_0_0t.f32(target("dx.RawBuffer", float, 1, 0, 0), i32, i32, float)
diff --git a/llvm/test/CodeGen/DirectX/RawBufferLoad.ll b/llvm/test/CodeGen/DirectX/RawBufferLoad.ll
index 37260326071bb..66713ef3e7d6d 100644
--- a/llvm/test/CodeGen/DirectX/RawBufferLoad.ll
+++ b/llvm/test/CodeGen/DirectX/RawBufferLoad.ll
@@ -36,12 +36,12 @@ define void @loadf32_byte(i32 %offset) {
       @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
           i32 0, i32 0, i32 1, i32 0, ptr null)
 
-  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f32 @dx.op.rawBufferLoad.f32(i32 139, %dx.types.Handle %{{.*}}, i32 %offset, i32 0, i8 1, i32 4)
+  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f32 @dx.op.rawBufferLoad.f32(i32 139, %dx.types.Handle %{{.*}}, i32 %offset, i32 undef, i8 1, i32 4)
   %load = call {float, i1}
       @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(
           target("dx.RawBuffer", i8, 0, 0, 0) %buffer,
           i32 %offset,
-          i32 0)
+          i32 poison)
   %data = extractvalue {float, i1} %load, 0
 
   ; CHECK: [[VAL:%.*]] = extractvalue %dx.types.ResRet.f32 [[DATA]], 0
@@ -85,12 +85,12 @@ define void @loadv4f32_byte(i32 %offset) {
       @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
           i32 0, i32 0, i32 1, i32 0, ptr null)
 
-  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f32 @dx.op.rawBufferLoad.f32(i32 139, %dx.types.Handle %{{.*}}, i32 %offset, i32 0, i8 15, i32 4)
+  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f32 @dx.op.rawBufferLoad.f32(i32 139, %dx.types.Handle %{{.*}}, i32 %offset, i32 undef, i8 15, i32 4)
   %load = call {<4 x float>, i1}
       @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(
           target("dx.RawBuffer", i8, 0, 0, 0) %buffer,
           i32 %offset,
-          i32 0)
+          i32 poison)
   %data = extractvalue {<4 x float>, i1} %load, 0
 
   ; CHECK: extractvalue %dx.types.ResRet.f32 [[DATA]], 0
@@ -212,9 +212,9 @@ define void @loadv4f64_byte(i32 %offset) {
       @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
           i32 0, i32 0, i32 1, i32 0, ptr null)
 
-  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f64 @dx.op.rawBufferLoad.f64(i32 139, %dx.types.Handle %{{.*}}, i32 %offset, i32 0, i8 15, i32 8)
+  ; CHECK: [[DATA:%.*]] = call %dx.types.ResRet.f64 @dx.op.rawBufferLoad.f64(i32 139, %dx.types.Handle %{{.*}}, i32 %offset, i32 undef, i8 15, i32 8)
   %load = call {<4 x double>, i1} @llvm.dx.resource.load.rawbuffer.v4i64(
-      target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset, i32 0)
+      target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset, i32 poison)
   %data = extractvalue {<4 x double>, i1} %load, 0
 
   ; CHECK: extractvalue %dx.types.ResRet.f64 [[DATA]], 0
diff --git a/llvm/test/CodeGen/DirectX/RawBufferStore.ll b/llvm/test/CodeGen/DirectX/RawBufferStore.ll
index 856f9d1034227..fa71f6d7ffcfa 100644
--- a/llvm/test/CodeGen/DirectX/RawBufferStore.ll
+++ b/llvm/test/CodeGen/DirectX/RawBufferStore.ll
@@ -22,10 +22,10 @@ define void @storef32_byte(i32 %offset, float %data) {
       @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
           i32 0, i32 0, i32 1, i32 0, ptr null)
 
-  ; CHECK: call void @dx.op.rawBufferStore.f32(i32 140, %dx.types.Handle %buffer_annot, i32 %offset, i32 0, float %data, float undef, float undef, float undef, i8 1, i32 4)
+  ; CHECK: call void @dx.op.rawBufferStore.f32(i32 140, %dx.types.Handle %buffer_annot, i32 %offset, i32 undef, float %data, float undef, float undef, float undef, i8 1, i32 4)
   call void @llvm.dx.resource.store.rawbuffer.f32(
       target("dx.RawBuffer", i8, 1, 0, 0) %buffer,
-      i32 %offset, i32 0, float %data)
+      i32 %offset, i32 poison, float %data)
 
   ret void
 }
@@ -58,10 +58,10 @@ define void @storev4f32_byte(i32 %offset, <4 x float> %data) {
   ; CHECK: [[DATA1:%.*]] = extractelement <4 x float> %data, i32 1
   ; CHECK: [[DATA2:%.*]] = extractelement <4 x float> %data, i32 2
   ; CHECK: [[DATA3:%.*]] = extractelement <4 x float> %data, i32 3
-  ; CHECK: call void @dx.op.rawBufferStore.f32(i32 140, %dx.types.Handle %buffer_annot, i32 %offset, i32 0, float [[DATA0]], float [[DATA1]], float [[DATA2]], float [[DATA3]], i8 15, i32 4)
+  ; CHECK: call void @dx.op.rawBufferStore.f32(i32 140, %dx.types.Handle %buffer_annot, i32 %offset, i32 undef, float [[DATA0]], float [[DATA1]], float [[DATA2]], float [[DATA3]], i8 15, i32 4)
   call void @llvm.dx.resource.store.rawbuffer.v4f32(
       target("dx.RawBuffer", i8, 1, 0, 0) %buffer,
-      i32 %offset, i32 0, <4 x float> %data)
+      i32 %offset, i32 poison, <4 x float> %data)
 
   ret void
 }
@@ -135,10 +135,10 @@ define void @storev4f64_byte(i32 %offset, <4 x double> %data) {
   ; CHECK: [[DATA1:%.*]] = extractelement <4 x double> %data, i32 1
   ; CHECK: [[DATA2:%.*]] = extractelement <4 x double> %data, i32 2
   ; CHECK: [[DATA3:%.*]] = extractelement <4 x double> %data, i32 3
-  ; CHECK: call void @dx.op.rawBufferStore.f64(i32 140, %dx.types.Handle %buffer_annot, i32 %offset, i32 0, double [[DATA0]], double [[DATA1]], double [[DATA2]], double [[DATA3]], i8 15, i32 8)
+  ; CHECK: call void @dx.op.rawBufferStore.f64(i32 140, %dx.types.Handle %buffer_annot, i32 %offset, i32 undef, double [[DATA0]], double [[DATA1]], double [[DATA2]], double [[DATA3]], i8 15, i32 8)
   call void @llvm.dx.resource.store.rawbuffer.v4i64(
       target("dx.RawBuffer", i8, 1, 0, 0) %buffer,
-      i32 %offset, i32 0, <4 x double> %data)
+      i32 %offset, i32 poison, <4 x double> %data)
 
   ret void
 }
diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll
index ae5e992184f4c..f3a2f6233d6eb 100644
--- a/llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll
@@ -38,7 +38,7 @@ define void @loadf32_byte(i32 %offset) {
   %ptr = call ptr @llvm.dx.resource.getpointer(
       target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset)
 
-  ; CHECK: %[[LOAD:.*]] = call { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset, i32 0)
+  ; CHECK: %[[LOAD:.*]] = call { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset, i32 poison)
   ; CHECK: %[[VAL:.*]] = extractvalue { float, i1 } %[[LOAD]], 0
   ; CHECK: call void @f32_user(float %[[VAL]])
   %data = load float, ptr %ptr
@@ -76,7 +76,7 @@ define void @loadv4f32_byte(i32 %offset) {
   %ptr = call ptr @llvm.dx.resource.getpointer(
       target("dx.RawBuffer", i8, 0...
[truncated]

@github-actions
Copy link

⚠️ undef deprecator found issues in your code. ⚠️

You can test this locally with the following command:
git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef([^a-zA-Z0-9_-]|$)|UndefValue::get)' 'HEAD~1' HEAD llvm/test/CodeGen/DirectX/RawBuffer-errors.ll llvm/lib/Target/DirectX/DXILOpLowering.cpp llvm/lib/Target/DirectX/DXILResourceAccess.cpp llvm/test/CodeGen/DirectX/BufferLoad-sm61.ll llvm/test/CodeGen/DirectX/BufferStore-sm61.ll llvm/test/CodeGen/DirectX/RawBufferLoad.ll llvm/test/CodeGen/DirectX/RawBufferStore.ll llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll

The following files introduce new uses of undef:

  • llvm/lib/Target/DirectX/DXILOpLowering.cpp
  • llvm/test/CodeGen/DirectX/BufferLoad-sm61.ll
  • llvm/test/CodeGen/DirectX/BufferStore-sm61.ll
  • llvm/test/CodeGen/DirectX/RawBufferLoad.ll
  • llvm/test/CodeGen/DirectX/RawBufferStore.ll

Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields undef. You should use poison values for placeholders instead.

In tests, avoid using undef and having tests that trigger undefined behavior. If you need an operand with some unimportant value, you can add a new argument to the function and use that instead.

For example, this is considered a bad practice:

define void @fn() {
  ...
  br i1 undef, ...
}

Please use the following instead:

define void @fn(i1 %cond) {
  ...
  br i1 %cond, ...
}

Please refer to the Undefined Behavior Manual for more information.

Copy link
Contributor

@kmpeng kmpeng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

return make_error<StringError>(
"Element index of structured buffer may not be poison",
inconvertibleErrorCode());
else if (!IsStruct && !IsPoison)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you might consider the variable name IsStructured if you want to disambiguate between structured resource types and structs as an element type.

uint64_t AccessSize = 1;
Value *Offset =
traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
// TODO: break up larger types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO taken care of now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #167542 - this TODO was there for raw buffer cases, but in practice because of how the pass pipeline is set up it isn't really possible to get larger types here. This patch handles the raw buffer cases that the TODO was intended for, despite not actually doing what the TODO says.

Copy link
Contributor

@bob80905 bob80905 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

return make_error<StringError>(
"Element index of structured buffer may not be poison",
inconvertibleErrorCode());
else if (!IsStructured && !IsPoison)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: clang-tidy will complain (and I suppose it seems so will I) if it sees an else after a return

Suggested change
else if (!IsStructured && !IsPoison)
if (!IsStructured && !IsPoison)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I like the else here since the condition parallels the previous one, but I don't feel strongly so I've updated it to make clang-tidy (and I suppose it seems you as well) happy here.

@bogner bogner merged commit ac602d8 into llvm:main Dec 23, 2025
7 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DirectX] Handle RawBuffer correctly in DXILResourceAccess

5 participants